package com.yyge.annotation.accessLimit;

import com.aliyun.oss.ServiceException;
import com.yyge.system.entity.SystemUser;
import com.yyge.util.AuthUtil;
import com.yyge.util.HttpUtil;
import com.yyge.util.StringUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.time.Duration;
import java.util.Objects;

/**
 * @program: js-hd
 * @description: 限流切面逻辑
 * @author: gityyge
 * @create: 2024-01-30 11:09
 **/
@Aspect
@Slf4j
@AllArgsConstructor
@Component
public class AccessLimitAspect {

    @Resource
    private AuthUtil authUtil;

    @Resource
    private RedisTemplate<Object,Object> redisTemplate;

    @Pointcut("@annotation(com.yyge.annotation.accessLimit.AccessLimit)")
    private void annotationPointCut(){

    }
    @Around("annotationPointCut() && @annotation(accessLimit)")
    public Object before(ProceedingJoinPoint joinPoint, AccessLimit accessLimit) throws Throwable {
        SystemUser user = authUtil.getUser();
        String key = HttpUtil.getIp()+"|"+HttpUtil.getUA();
        String md5 = StringUtil.getMd5(key);

        //先获取一下方法名称 全路径
        String class_name = joinPoint.getTarget().getClass().getName(); //获取注解所在类的类名
        String method_name = joinPoint.getSignature().getName(); //获取注解所在类的方法名
        String redisMark="limit:"+md5+":"+class_name+"."+method_name; //生成redis的标识 方便查询次数

	    // 现在是要根据ID去做标识
        //判断注解是否开启限制-如果开启限制则从Redis中查询数据 查看登记的次数
        Object num = redisTemplate.opsForValue().get(redisMark);
         if(Objects.isNull(num)){ //如果redis里面没有记录值 则记录
            long maxNumber= accessLimit.second();
            redisTemplate.opsForValue().set(redisMark,1,Duration.ofSeconds(maxNumber));
         }else{
             // 如果max是10次 则第11次就会提示错误
             if((Integer) num >= accessLimit.max()){
                 throw new RuntimeException("请勿频繁访问该接口"); //错误提示
             }else{
                 //获取剩余过期时间
                 Long expire = redisTemplate.getExpire(redisMark);
                 Integer tempNum=(Integer) num+1;
                 redisTemplate.opsForValue().set(redisMark,tempNum,Duration.ofSeconds(expire));
                 //重新设置值以及过期时间
             }
         }
        //返回并执行正常的controller 内的内容
        return joinPoint.proceed();
        //返回并执行正常的controller 内的内容
    }
}
